#include <stdio.h>
#include <stdlib.h>

#include "sample_comm.h"

#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"

#include "HisiEncGetStream.h"

#define HISI_ENC_GET_STREAM_STACK_SIZE        (10 * 1024 * 1024)

#define HIENC_ASSERT_RETURN(expr,retval)    \
        do { \
        if (!(expr)) { assert(expr); return retval; } \
        } while (0)

enum enc_out_tube
{
    ENC_OUT_ES = 0,
    ENC_OUT_NUMS
};

typedef struct
{
    OD_TASK_COMMON_MEMBERS

    HI_U32 VeChn;
    struct {
        fd_set read_fds;
        HI_S32 VencFd;
        VENC_CHN_ATTR_S stVencChnAttr;
    }himpp;

    HI_U32 getCnt;
}hisiEncGetStream_obj;

/* dynamic_params */
params_def_t hisiEncGetStream_dynamic_params_def[] = 
{
    {OFFSET(hisiEncGetStream_dynamic_params, ctrl), ODPT_VAR_INT32 | PT_REAL_TIME, sizeof(int), OD_OFF, OD_ON},
    {0, 0, 0, 0, 0}
};

const hisiEncGetStream_static_params glb_hisiEncGetStream_static_params_default = 
{
    "hisi_decoder",   /*task_name*/
    0,                /*VeChn*/
    (10 * 1024),      /*bufsize*/
    50                /*buf_nums*/
};

const hisiEncGetStream_dynamic_params glb_hisiEncGetStream_dynamic_params_default = 
{
    OD_ON         //ctrl
};


static void hisiEncGetStream_get_stream(hisiEncGetStream_obj *obj, VENC_STREAM_S* pstStream)
{
    tube_handle ipc_out = obj->outs.tubes[ENC_OUT_ES];
    hbuf_handle hbuf = NULL;
    HI_S32 i, es_len, ret, buf_size;
    char *dst = NULL, *src = NULL;
    static HI_U32 cnt = 0;

    for (i = 0; i < pstStream->u32PackCount; i++)
    {
        es_len = pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset;
        src = pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset;

        /***********************************************
        * get available buffer from the output IPC
        ***********************************************/
        ret = tube_get_buf(ipc_out, &hbuf);
        if (ret != OD_OK) {
            RPTERR("tube_get_buf error");
            return;
        }

        dst = (char *)hbuf_get_useptr(hbuf);
        buf_size = hbuf_get_size(hbuf);
        if (NULL == dst 
             || 0 == buf_size) {
            tube_put_buf(ipc_out, hbuf);
            RPTERR(" buf NULL or buf_size == 0!!,error! ");
            return;
        }

        if (es_len > buf_size) {
            RPTERR("es_len:%d > buf_size:%d, error!!!", es_len, buf_size);
            tube_put_buf(ipc_out, hbuf);
            return;
        }

        memcpy(dst, src, es_len);
        hbuf_set_usedsize(hbuf, es_len);
        hbuf_set_ts(hbuf, ((pstStream->pstPack[i].u64PTS / 1000) * 90));
        tube_put_buf(ipc_out, hbuf); 

        if ((cnt++ % 100) == 0)
            RPTINF("~PUT A BUF OUT count <%u>", cnt);
    }
}


static int hisiEncGetStream_start_stop(hisiEncGetStream_obj *obj)
{
    VENC_CHN VencChn;
    HI_S32 s32Ret;

    /* decide the stream file name, and open file to save stream */
    VencChn = obj->VeChn;
    s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &obj->himpp.stVencChnAttr);
    if (s32Ret != HI_SUCCESS)
    {
        RPTERR("HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!\n", \
                    VencChn, s32Ret);
        od_msleep(10);
        return OD_ERROR;
    }

    /* Set Venc Fd. */
    obj->himpp.VencFd = HI_MPI_VENC_GetFd(VencChn);
    if (obj->himpp.VencFd < 0)
    {
        RPTERR("HI_MPI_VENC_GetFd failed with %#x!\n",
                                        obj->himpp.VencFd);
        od_msleep(10);
        return OD_ERROR;
    }

    return OD_OK;
}

static int hisiEncGetStream_process(hisiEncGetStream_obj *obj)
{
    struct timeval TimeoutVal;
    HI_S32 s32Ret;
    VENC_STREAM_S stStream;
    VENC_CHN_STATUS_S stStat;

    FD_ZERO(&obj->himpp.read_fds);
    FD_SET(obj->himpp.VencFd, &obj->himpp.read_fds);

    TimeoutVal.tv_sec  = 2;
    TimeoutVal.tv_usec = 0;
    s32Ret = select(obj->himpp.VencFd + 1, &obj->himpp.read_fds, NULL, NULL, &TimeoutVal);
    if (s32Ret < 0)
    {
        RPTERR("select failed!\n");
        return OD_ERROR;
    }
    else if (s32Ret == 0)
    {
        RPTINF("get venc stream time out!!\n");
        return OD_ERROR;
    }

    if (!FD_ISSET(obj->himpp.VencFd, &obj->himpp.read_fds)) {
        RPTDBG("FD_ISSET!");
        return OD_OK;
    }

    /*******************************************************
     step 2.1 : query how many packs in one-frame stream.
    *******************************************************/
    memset(&stStream, 0, sizeof(stStream));
    s32Ret = HI_MPI_VENC_QueryStatus(obj->VeChn, &stStat);
    if (HI_SUCCESS != s32Ret)
    {
        RPTERR("HI_MPI_VENC_QueryStatus chn[%d] failed with %#x!\n", obj->VeChn, s32Ret);
        od_msleep(50);
        return OD_ERROR;
    }

    /*******************************************************
    step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example:
     if(0 == stStat.u32CurPacks || 0 == stStat.u32LeftStreamFrames)
     {
        SAMPLE_PRT("NOTE: Current  frame is NULL!\n");
        continue;
     }
    *******************************************************/
    if(0 == stStat.u32CurPacks)
    {
        RPTWRN("NOTE: Current  frame is NULL!\n");
        return OD_OK;
    }
    
    /*******************************************************
     step 2.3 : malloc corresponding number of pack nodes.
    *******************************************************/
    stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);
    if (NULL == stStream.pstPack)
    {
        RPTERR("malloc stream pack failed!\n");
        return OD_ERROR;
    }

    /*******************************************************
     step 2.4 : call mpi to get one-frame stream
    *******************************************************/
    stStream.u32PackCount = stStat.u32CurPacks;
    s32Ret = HI_MPI_VENC_GetStream(obj->VeChn, &stStream, HI_TRUE);
    if (HI_SUCCESS != s32Ret)
    {
        free(stStream.pstPack);
        stStream.pstPack = NULL;
        RPTERR("HI_MPI_VENC_GetStream failed with %#x!\n", \
                   s32Ret);
        return OD_OK;
    }

    /*******************************************************
     step 2.5 : get fream send to stream pack thread
    *******************************************************/
    hisiEncGetStream_get_stream(obj, &stStream);
    
    /*******************************************************
     step 2.6 : release stream
     *******************************************************/
    s32Ret = HI_MPI_VENC_ReleaseStream(obj->VeChn, &stStream);
    if (HI_SUCCESS != s32Ret)
    {
        RPTERR("HI_MPI_VENC_ReleaseStream failed!\n");
        free(stStream.pstPack);
        stStream.pstPack = NULL;
        return OD_ERROR;
    }

    /*******************************************************
     step 2.7 : free pack nodes
    *******************************************************/
    free(stStream.pstPack);
    stStream.pstPack = NULL;

    RPTDBG("get count :%u", obj->getCnt++);
    return OD_OK;
}


hisiEncGetStream_handle hisiEncGetStream_create(hisiEncGetStream_static_params *static_params,
                                                  hisiEncGetStream_dynamic_params *dynamic_params)
{
    hisiEncGetStream_obj *obj = NULL;
    hisiEncGetStream_dynamic_params *params = NULL;
    tube_attr_t tube_attr = glb_tube_Attrs_default;

    HIENC_ASSERT_RETURN(static_params, NULL);

    obj = od_task_create((char *)static_params->task_name, sizeof(hisiEncGetStream_obj));
    HIENC_ASSERT_RETURN(obj, NULL);

    obj->tab = hisiEncGetStream_dynamic_params_def;
    obj->params_nums = sizeof(hisiEncGetStream_dynamic_params_def) / sizeof(params_def) - 1;

    params = od_calloc(1, sizeof(hisiEncGetStream_dynamic_params));
    HIENC_ASSERT_RETURN(params, NULL);

    if (NULL == dynamic_params)
        memcpy(params, &glb_hisiEncGetStream_dynamic_params_default, sizeof(hisiEncGetStream_dynamic_params));
    else
        memcpy(params, dynamic_params, sizeof(hisiEncGetStream_dynamic_params));

    obj->VeChn = static_params->VeChn;

    /* output tubes */
    obj->outs.nums = ENC_OUT_NUMS;
    obj->outs.tubes = od_calloc(1, obj->outs.nums * sizeof(tube_handle));

    tube_attr.type = OD_TUBE_TYPE_PRODUCER;
    tube_attr.bufsize = static_params->bufsize;
    tube_attr.buf_nums = static_params->buf_nums;
    tube_attr.del_after_free = OD_OFF;

    strncpy(tube_attr.name, ENC_GET_STREAM_NAME, OD_MAX_OBJ_NAME - 1);
    obj->outs.tubes[ENC_OUT_ES] = tube_create(&tube_attr);
    
    obj->params = (void *)params;
    obj->stack_size = HISI_ENC_GET_STREAM_STACK_SIZE;
    obj->pf_start_stop  = (void *)hisiEncGetStream_start_stop;
    obj->pf_malloc_free = NULL;
    obj->pf_rt_set = NULL;
    obj->pf_process     = (void *)hisiEncGetStream_process;
    obj->pf_malloc_free_delete = NULL;
    obj->pf_start_stop_delete = NULL;
    
    RPTDBG("create success!");
    return (hisiEncGetStream_handle)obj;

}



/*_*/

